#include "StdAfx.h"
#include <cmath>
#include "automata.h"
#include "constant.h"

const char _K_ZERO_[]="0";
const char _K_PI[]="3.1415926535897932384626433832795";
const char _K_E[] ="2.7182818284590452353602874713527";
#ifndef M_PI
#define M_PI (3.1415926535897932384626433832795)
#endif

const int _STATE_O_=0xF0;	//
const int _STATE_A_=0xF1;	// No Dot
const int _STATE_B_=0xF2;	// Dot
const int _STATE_C_=0xF3;	// Single Operation, 1/x,sin,cos,...
const int _STATE_D_=0xF4;	// Binary,..., + - * / ^
const int _STATE_E_=0xF5;	// ( clicked
const int _STATE_ERR_=-0xFF;

double Fact(double x);//Fact(n)=n!
/*
 *	初始化
 *  清零
 */
CAutoMata::CAutoMata() {
    this->mState=_STATE_O_;
    this->mDisplay=_K_ZERO_;
    this->err=false;
}
CAutoMata::~CAutoMata() {
    //Nothing TODO
}
/*
 *	默认为弧度
 */
#include <string>
int CAutoMata::acceptInstruction(int data){
    return CAutoMata::acceptInstruction(data, RAD);

}
/*********************************************************
 * 状态转移、计算功能的实现的实现
 *********************************************************/ 
int CAutoMata::acceptInstruction(int data, int type){
    if(err && data!=kReset)
        return ST_IGNORE;
    int tmp_len;/*Warning... Do Not Use it*/
    double value=mDisplay.toDouble();
    switch (data) {
/*********************************************************/ 
        case '0':
            switch (mState) {
                case _STATE_O_:
                    return ST_IGNORE;	//直接忽略
                case _STATE_A_:
                case _STATE_B_:
                    this->mDisplay.append(_K_ZERO_);
                    return ST_UPDATE_DISPLAY;//更新显示
                case _STATE_C_:
                case _STATE_D_:
                    this->mState=_STATE_O_;
                    this->mDisplay=_K_ZERO_;
                    return ST_UPDATE_DISPLAY;
            }
            break;
/*********************************************************/ 
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            switch (mState) {
                case _STATE_O_:
                case _STATE_C_:
                case _STATE_D_:
                    mDisplay.clear();
                    mDisplay.append(data);
                    mState=_STATE_A_;
                    return ST_UPDATE_DISPLAY;
                case _STATE_A_:mDisplay.append(data);break;
                case _STATE_B_:
                    mDisplay.append(data);
                    /****************************************
                     *		(*^_^*)	East Egg Here!			*
                     *		Don't Tell Others,Ha ha 		*
                     ****************************************/
                    if(mDisplay.compare(_K_PI)==0)
                        return __EASTER_EGG__;
                    return ST_UPDATE_DISPLAY;
            }
            break;
/*********************************************************/ 
        case kDot:
            switch(mState){
                case _STATE_O_:
                case _STATE_A_:
                    mDisplay.append('.');
                    mState=_STATE_B_;
                    return ST_UPDATE_DISPLAY;
                case _STATE_B_:
                    return ST_IGNORE;// X.... => X.忽略多余的小数点
                case _STATE_C_:
                case _STATE_D_:
                    mDisplay.clear();
                    mDisplay.append('0');
                    mDisplay.append('.');
                    mState=_STATE_B_;
                    return ST_UPDATE_DISPLAY;
            }
            break;
/*********************************************************/ 
        case kAdd:
        case kMinus:
        case kMuliply:
        case kDivide:
        case kMOD:
        case kPower:
            switch (mState)	{
                /*	忽略多余的运算符，保留最后输入的
                 *	If input 4+-+-*-3
                 *	We will ignore  the previous operators
                 *  Only Calculate 4-3
                 */
                case _STATE_D_:
                    this->mStackOperator.pop();
                    this->mStackOperator.push(data);
                    return ST_IGNORE;
            }
            
            updateStack(data);
            this->mState=_STATE_D_;
            return ST_UPDATE_DISPLAY;
            break;
/*********************************************************/ 
        case kLeftBracket:
            switch(mState){
                case _STATE_O_:
                case _STATE_D_:
                    mState=_STATE_E_;
                    mStackOperator.push(kLeftBracket);
                    return ST_UPDATE_BRACKET;
                    break;
                case _STATE_A_:
                case _STATE_B_:
                case _STATE_C_:break;
            }
/*********************************************************/ 
        case kSquareRoot:
            if (value<0){
                this->error();
                return ST_UPDATE_DISPLAY;
            }
            value=sqrt( value );
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kFactorial:
            if (value<0||value>70){//阶乘参数范围
                this->error();
                return ST_UPDATE_DISPLAY;
            }
            value=Fact(value);
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kReciprocal:
            if (value==0.0)	{	//0没有倒数
                mState=_STATE_ERR_;
                this->error();
                return ERR_DIV_BY_ZERO;
            }
            value=1.0/( value );
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kCaculate:
            updateStack(kCaculate);
            mState=_STATE_C_;
            break;
        case kSign:
            if (!mDisplay.compare(_K_ZERO_))
                break;
            if (mDisplay.at(0)=='-')
                mDisplay.deletefirst();
            else
                mDisplay.insertfirst('-');
            break;
        case kSine:
            if(type==DEG){
                value=value/180*M_PI;
            }
            else if(type==GRD){
                value=value/200*M_PI;
            }
            value=sin( value );
            
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kArcSine:
            if(fabs(value)>1.0){
                this->error();
                break;
            }
            value=asin( value );
            if(type==DEG)
                value=value*180/M_PI;
            else if(type==GRD)
                value=value*200/M_PI;
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kCosine:
            
            if(type==DEG){value=value/180*M_PI;}
            else
                if(type==GRD){value=value/200*M_PI;}
            value=cos( value );
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kArcCosine:
            if(fabs(value)>1.0){
                this->error();
                break;
            }
            value=acos( value );
            if(type==DEG)
                value=value*180/M_PI;
            else if(type==GRD)
                value=value*200/M_PI;
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kTangent:
            /*************************************************
             * tangent (n+1/2)pi ==> infinate >_<            *
             *************************************************/
            if(type==DEG){value=value/180*M_PI;}
            else
                if(type==GRD){value=value/200*M_PI;}
            if (fabs(fmod(fabs(value/M_PI), 1)-0.5)<1E-10) {
                this->error();
            }
            value=tan( value );\
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
            
        case kArcTangent:
            value=atan( value );
            
            if(type==DEG)
                value=value*180/M_PI;
            else if(type==GRD)
                value=value*200/M_PI;
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kLog10:
            if(value<=0.0){
                this->error();
                mState=_STATE_ERR_;
            }
            value=log10(value);
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kLn:
            if(value<=0.0){
                this->error();
                mState=_STATE_ERR_;
            }
            value=log(value);
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kSquare:
            value*= value ;
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kCube:
            value= value*value*value ;
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kClearCurrent:
            mDisplay=_K_ZERO_;
            mState=_STATE_O_;
            break;
        case kConstantPI:
            mDisplay=_K_PI;
            mState=_STATE_C_;
            break;
        case kConstantE:
            mDisplay=_K_E;
            mState=_STATE_C_;
            break;
        case kToInteger:
            value= floor(value) ;
            //value=stringToDouble(mDisplay);
            
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
/*********************************************************/ 
        case kReset:
            this->reset();
        case kBackSpace:
            switch (mState) {
                case _STATE_A_:
                case _STATE_B_:
                    tmp_len=mDisplay.size()-1;
                    if (mDisplay.at(tmp_len)=='.') {
                        mState=_STATE_A_;
                    }
                    mDisplay.deletelast();
                    if (tmp_len==0
                    || 0==mDisplay.compare("-")
                    || 0==mDisplay.compare("-0")
                    || 0==mDisplay.compare("0")) {
                        mDisplay=_K_ZERO_;
                        mState=_STATE_O_;
                    }
                    break;
                default:break;
            }
            break;
/************************************************************************/
        case kMemoryAdd:
            memory.Add(value);mState=_STATE_C_;return ST_UPDATE_MEMORY;break;
        case kMemoryClear:
            memory.Clear();mState=_STATE_C_;return ST_UPDATE_MEMORY;break;
        case kMemoryRead:
            value=memory.Read();
            mDisplay.fromDouble(value);
            mState=_STATE_C_;
            break;
        case kMemorySave:
            memory.Save(value);mState=_STATE_C_;return ST_UPDATE_MEMORY;break;
    }
    return ST_UPDATE_DISPLAY;
}

bool CAutoMata::hasMemoryStored(){
    return memory.isStored();
}
//************************************
// FullName:  CAutoMata::getDisplay
// Access:    public 
// Returns:   mystring
// 返回计算结果
// 出错则返回"ERROR"
//************************************
mystring CAutoMata::getDisplay(){
    static char *error="Error";
    if (this->err) {
        this->mDisplay.clear();
        this->mDisplay.append(error);
        return mDisplay;
    }
    int len=mDisplay.size();
    if(len==0 || len==1
    || mState==_STATE_B_
    || mState==_STATE_A_
    || mState==_STATE_O_)
        return mDisplay;
    return mDisplay;
}
/************************************************************************
 *              遇到运算符(+,-,*,/,MOD,^)更新栈                         *
 *				遇到除0则出错，停止计算									*			
 ************************************************************************/
void CAutoMata::updateStack(int op){
    int op_level(int op);	
    int level=op_level(op);   //当前操作符优先级
    double top=mDisplay.toDouble();
    this->mStackOperand.push(top);

    if(!mStackOperator.empty()){
		//判断优先级和栈顶优先级的大小
        if(level<=op_level(this->mStackOperator.top())){
            while (mStackOperator.size()){
                int top_op=mStackOperator.top();	//std::stack::pop()函数没有返回值
				mStackOperator.pop();				//所以调用两个函数来实现这一功能
                
                long double s2=mStackOperand.top();
                mStackOperand.pop();
                
                long double s1= mStackOperand.top();
                mStackOperand.pop();
                //判断是那种运算，并将计算结果压到操作数栈顶
                switch(top_op){
                    case kAdd		:mStackOperand.push(s1+s2);break;	//加法
                    case kMinus		:mStackOperand.push(s1-s2);break;	//减法
                    case kMuliply	:mStackOperand.push(s1*s2);break;	//乘法
                    case kDivide	:if(s2==0.0){err=true;return;};		//除法
										mStackOperand.push(s1/s2);
										break;	
                    case kMOD		:if(s2==0.0){err=true;return;};		//求余数
									mStackOperand.push(fmod(s1, s2));
									break;
                    case kPower		:mStackOperand.push(pow(s1, s2));break;	//乘方
                    default:break;
                }//end switch
            }//end while
        }//end if level<=...
    }// end if not empty
    top=mStackOperand.top();
    if(op!=kCaculate){	//如果不是是 "等号" ,进入D状态
        this->mStackOperator.push(op);
        mState=_STATE_D_;
    }else{				//否则进入O状态
        this->mState=_STATE_O_;
        mStackOperand.pop();
    }
	//计算结果转换为字符串
    mDisplay.fromDouble(top);
}

/************************************************************************
 *	运算符优先级
 *	加法、加法1级
 *	乘法、除法2级
 *	幂运算    3级
 *	等于号	  0级
 ************************************************************************/
int op_level(int op){
    if(op==kCaculate)
        return 0x00;
    if(op==kPower)
        return 0xf0;
    if (op==kAdd || op==kMinus) {
        return 0x01;
    }else{
        return 0x02;
    }
}
void CAutoMata::reset(){
    //this->mStackOperand.clear();
    //this->mStackOperator.clear();
    while(!mStackOperand.empty()) {
        mStackOperand.pop();
    }
    while(!mStackOperator.empty()){
        mStackOperator.pop();
    }
    this->mDisplay=_K_ZERO_;
    this->mState=_STATE_O_;
    this->err=false;
}
void CAutoMata::error(){
    this->err=true;
}
void CAutoMata::trim0(){
    if (this->mDisplay.constain('e')) {
        return;
    }
    int len=this->mDisplay.size();
    while(mDisplay.at(--len)=='0')
        mDisplay.deletelast();
    if(mDisplay.at(len)=='.')
        mDisplay.deletelast();
}
/**************************************************
 * 阶乘
 **************************************************/ 
double Fact(double x){
    if(x==0)
        return 1.0;
    double f=1.0;
    while(x>=1){
        f=f*x;
        x--;
    }
    return f;
}
